package com.conversationboard.subscriptions;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.mail.MessagingException;
import javax.naming.NamingException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;

import org.joda.time.DateTime;

import com.conversationboard.cache.newanswers.NewAnswersThread;
import com.conversationboard.config.Configuration;
import com.conversationboard.config.ConnectionStrategy;
import com.conversationboard.email.Mail;
import com.conversationboard.model.Board;
import com.conversationboard.model.Boards;
import com.conversationboard.model.StyleSheets;
import com.conversationboard.model.User;
import com.conversationboard.model.subscriptions.Subscription;
import com.conversationboard.model.subscriptions.Subscriptions;

/**
 * Class to generate a message to be sent to users as part of subscriptions, for a given board.
 * 
 * (1) Get the list of subscriptions
 * (2) Produce a superset of all the boards subscribed to
 * (3) For each board, produce the XML document describing today's changes (or at most, what's in the new answers cache)
 * (4) Get the list of subscriptions again and produce an email to each user, tacking together each board XML
 * (5) Use XSLT to process the final XML for each user into a single HTML document
 * (5) Send the HTML document out as email to each user
 * 
 * @author Keith Watson
 *
 */

public class ProcessBoardSubscriptions {

	public ProcessBoardSubscriptions() {

	}


	public void processSubscriptions() throws SQLException, TransformerConfigurationException, FileNotFoundException, TransformerException, MessagingException, NamingException, IOException {

		List<Integer> boardIDs = Subscriptions.getSupersetOfSubscribedBoards();

		Map<Integer, String> xmlDocuments = this.generateXmlDocuments(boardIDs);

		this.produceEmails(xmlDocuments);

	}


	/**
	 * (2) For the passed in boards, get the threads with new answers that were today and return a chunk of XML describing
	 * them for subsequent processing into an email.
	 * 
	 * @param board
	 * @return
	 */

	private Map<Integer, String> generateXmlDocuments(List<Integer> boardIDs) throws SQLException {

		Map<Integer, String> xmlDocuments = new HashMap<Integer, String>();

		for (int boardId : boardIDs) {

			Board board = Boards.getBoard(boardId);
			List<NewAnswersThread> threads = board.getThreadsWithNewAnswers();

			StringBuilder buffer = new StringBuilder();

			buffer.append("<Board>");
			buffer.append("<Id>" + board.getBoardId() + "</Id>");
			buffer.append("<Name>" + board.getName() + "</Name>");
			buffer.append("<Threads>");

			int threadCount = 0;

			for (NewAnswersThread thread : threads) {

				DateTime lastUpdated = new DateTime(thread.getLastUpdated());

				/* Don't process threads last updated more than 24 hours ago */

				if (lastUpdated.plusHours(24).isBefore(new DateTime())) {
					continue;
				}

				/* Don't process deleted threads */

				if (thread.isDeleted()) {
					continue;
				}

				threadCount++;

				buffer.append("<Thread>");
				buffer.append("<BoardId>");
				buffer.append(thread.getBoardId());
				buffer.append("</BoardId>");
				buffer.append("<ThreadId>");
				buffer.append(thread.getThreadId());
				buffer.append("</ThreadId>");
				buffer.append("<Title>");
				buffer.append(thread.getTitle());
				buffer.append("</Title>\n");
				buffer.append("</Thread>");
			}

			buffer.append("</Threads>");
			buffer.append("</Board>");

			/* Don't add this board if there are no recent threads updated */

			if (threadCount == 0) {
				buffer = new StringBuilder();
			} else {
				xmlDocuments.put(board.getBoardId(), buffer.toString());
			}
		}

		return xmlDocuments;
	}


	/* This method holds a database connection for the length of time that it takes to send all the emails. The alternative
	 * is to hold all the emails in memory or write them to a file, neither of which I want to do right now. May need
	 * to make some changes at some point. */

	private void produceEmails(Map<Integer, String> xmlDocuments) throws SQLException, TransformerConfigurationException, FileNotFoundException, TransformerException, MessagingException, NamingException, IOException {

		/* (1) Get all the subscriptions */

		Map<String, Subscription> subscriptions = Subscriptions.getAll();

		/* (2) Get a handle on the emailer */

		Mail mail = new Mail();

		/* (3) For each individual, tack together an email and send it out */

		int documentCount = 0;

		for (String loginId : subscriptions.keySet()) {

			User user = User.get(loginId);
			Subscription subscription = subscriptions.get(loginId);

			StringBuilder buffer = new StringBuilder();

			buffer.append("<UpdatedThreads>");
			String URI = "http://" + Configuration.getInstance().getDomainName() + Configuration.getInstance().getRoot();
			buffer.append("<URI>" + URI + "</URI>");

			for (int boardId : subscription.getBoards()) {

				/* Only add these documents if the user is authorised on this board */

				if (!user.canRead(boardId)) {
					continue;
				}

				String xmlDocument = xmlDocuments.get(boardId);

				if (xmlDocument != null) {
					buffer.append(xmlDocument);
					documentCount++;
				}

			}

			buffer.append("</UpdatedThreads>");

			String emailContent = this.processEmail(buffer.toString());

			/* Only send an email if there's going to be something in it */

			if (documentCount > 0) {
				mail.send("noreply@noreply.com", loginId, "Today's Updated Threads", emailContent);
			}
		}
	}


	private String processEmail(String xmlDocument) throws TransformerConfigurationException, FileNotFoundException, TransformerException, IOException {

		String email = "Removed XSLT processor. Subscription emails no longer work\n\n" + xmlDocument;
		
		return email;

	}


	public static void main(String[] args) throws Exception {

		Configuration.load();
		StyleSheets.setStylesheetsPath("/Users/Keith/dev/workspaces/ilx3/ILX/WebContent/Stylesheets");
		Configuration.getInstance().setConnectionStrategy(ConnectionStrategy.StaticConnection);

		ProcessBoardSubscriptions subscriptions = new ProcessBoardSubscriptions();
		subscriptions.processSubscriptions();

	}

}
